package utils;

import java.util.ArrayList;

public class Arbol {
	 private String nombre;
	 private Arbol hd;
	 private Arbol hi;
	 private Arbol padre;
	 private int profundidad;
	 private int numNodos;
	 private boolean hoja;
	 private boolean raiz;
	 private boolean esHi;
	 private int profTotal;
	
	 public Arbol() {
		super();
		nombre=null;
		hd = null;
		hi = null;
		profundidad = 0;
		numNodos = 1;
		hoja = false;
		raiz = true;
		esHi = false;
	}
	 
	 public Arbol(ArrayList<String> cjtoFuns,  ArrayList<String> cjtoTerms,
			  int hmax, int prof, Arbol pater, boolean esHizq, boolean esRaiz){
		
		int nuevaProf = prof + 1;
		boolean  rnd=SRandom.nextBoolean();
		
		profundidad = prof;
		padre = pater;
		esHi = esHizq;
		raiz = esRaiz;
		if (padre == null) raiz = true;
		numNodos = 1;
		if (( (rnd) || (profundidad + 1 == hmax) 
			|| (padre != null && ("MP".equals(padre.nombre) || "MM".equals(padre.nombre))))
			&& (padre != null && !"DU".equals(padre.nombre) && !"NOT".equals(padre.nombre) && !"EQ".equals(padre.nombre)))
		{
		  //insertamos hoja
		  int intRand = SRandom.nextInt(cjtoTerms.size());
		  nombre = cjtoTerms.get(intRand);
		  hoja = true;
		}
		else
		{
		  
		  //insertamos funcion.	
			int intRand2;
			// Hay que ir terminando
		  if (profundidad + 2 == hmax)
			  intRand2 = SRandom.nextInt(2);
		  else intRand2 = SRandom.nextInt(cjtoFuns.size());
		  nombre = cjtoFuns.get(intRand2);
		  
		  hi = new Arbol(cjtoFuns, cjtoTerms, hmax, nuevaProf, this, true, false);
		  numNodos = numNodos + hi.getNumNodos();
		  
		  if ( /*(padre != null) &&*/ (nombre.equals("DU") || nombre.equals("EQ")))
		  {  
		      hd = new Arbol(cjtoFuns, cjtoTerms, hmax, nuevaProf, this, false, false);
		      numNodos = numNodos + hd.getNumNodos();
		      
		  }
		  hoja = false;
		}
	}

	 @Override
	public String toString() {
		return toString(this, 0);
	}
	 
	private String toString(Arbol arbolActual, int profundidad) {
		if (arbolActual == null)
			return "";
		
		String str = "";
		for (int iProf = 0; iProf < profundidad; iProf++)
			str += "  ";
		str += arbolActual.nombre + "\n";
		str += toString(arbolActual.hi, profundidad + 1);
		str += toString(arbolActual.hd, profundidad + 1);
		
		return str;
	}

	public String getNombre() {
		return nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public Arbol getHd() {
		return hd;
	}

	public void setHd(Arbol hd) {
		this.hd = hd;
	}

	public Arbol getHi() {
		return hi;
	}

	public void setHi(Arbol hi) {
		this.hi = hi;
	}

	public Arbol getPadre() {
		return padre;
	}

	public void setPadre(Arbol padre) {
		this.padre = padre;
	}

	public int getProfundidad() {
		return profundidad;
	}

	public void setProfundidad(int profundidad) {
		this.profundidad = profundidad;
	}

	public int getNumNodos() {
		return numNodos;
	}

	public void setNumNodos(int numNodos) {
		this.numNodos = numNodos;
	}

	public boolean isHoja() {
		return hoja;
	}

	public void setHoja(boolean hoja) {
		this.hoja = hoja;
	}

	public boolean isRaiz() {
		return raiz;
	}

	public void setRaiz(boolean raiz) {
		this.raiz = raiz;
	}

	public boolean isEsHi() {
		return esHi;
	}

	public void setEsHi(boolean esHi) {
		this.esHi = esHi;
	}

	public int getProfTotal() {
		return profTotal;
	}

	public void setProfTotal(int profTotal) {
		this.profTotal = profTotal;
	}
	
	/*
	public Arbol GetArbolPosicion(Arbol a,int posicionbusqueda){
	    if (posicionbusqueda == 0 || a.getPos()== posicionbusqueda ){
		    return a;
		    }
	    else {		
	    	 if ( (a.getNombre().equals("EQ")) || (a.getNombre().equals("DU"))) {
	    		 if(posicionbusqueda <= a.getPos()+a.getHi().getNumNodos())
	    			 return GetArbolPosicion(a.getHi(),posicionbusqueda);
	    		 else
	    			 return GetArbolPosicion(a.getHd(),posicionbusqueda);
	    	 }
	    	 
	    	 if ((a.getNombre().equals("NOT")) || (a.getNombre().equals("MP")) || (a.getNombre().equals("MM"))) {
				return GetArbolPosicion(a.getHi(),posicionbusqueda);
				}
	    	 else{
	    		 System.out.println("Error al buscar el nodo");
	    		 return a;
	    	 }
		}	
}
	*/
	/*
	public void actualizarPosicion()
	{
		ArrayList<Arbol> inorden = inorden();
		for (int i =0 ; i< inorden.size(); i++)
			inorden.get(i).setPos(i);
	}
	*/
	
	public int calcularProfundidad() {
		int profHI = (hi == null)? 0 : hi.calcularProfundidad();
		int profHD = (hd == null)? 0 : hd.calcularProfundidad();
		
		profTotal = Math.max(profHI, profHD) + 1;
		return profTotal;
	}
    
    public ArrayList<Arbol> inorden() {
		ArrayList<Arbol> inorden = new ArrayList<Arbol>();
		inorden(inorden, this);
		
		return inorden;
	}

	//preorden Sin Raiz
	private static void inorden(ArrayList<Arbol> lista, Arbol arbolActual) {
		if (arbolActual != null) {
			lista.add(arbolActual);
			inorden(lista, arbolActual.getHi());
			inorden(lista, arbolActual.getHd());
		}
	}
	
	public ArrayList<Arbol> preordenSR() {
		ArrayList<Arbol> preorden = new ArrayList<Arbol>();
		preordenSR(preorden, this);
		
		return preorden;
	}
	
	//preorden Sin Raiz
	private static void preordenSR(ArrayList<Arbol> lista, Arbol arbolActual) {
		if (arbolActual != null) {
			preordenSR(lista, arbolActual.getHi());
			if (arbolActual.getPadre() != null)
				lista.add(arbolActual);
			preordenSR(lista, arbolActual.getHd());
		}
	}
	
	public ArrayList<Arbol> preordenTerminalesSR() {
		ArrayList<Arbol> preorden = new ArrayList<Arbol>();
		preordenTerminalesSR(preorden, this);
		
		return preorden;
	}
	
	private static void preordenTerminalesSR(ArrayList<Arbol> lista, Arbol arbolActual) {
		if (arbolActual != null) {
			preordenTerminalesSR(lista, arbolActual.getHi());
			if ((arbolActual.getNombre().equals("CP") || 
				arbolActual.getNombre().equals("BS") || 
				arbolActual.getNombre().equals("SN")) &&
				arbolActual.getPadre() != null)
				lista.add(arbolActual);
			preordenTerminalesSR(lista, arbolActual.getHd());
		}
	}
	
	public ArrayList<Arbol> preordenFuncion() {
		ArrayList<Arbol> preorden = new ArrayList<Arbol>();
		preordenOpSR(preorden, this);
		
		return preorden;
	}
	
	
	private void preordenOpSR(ArrayList<Arbol> lista, Arbol arbolActual) {
		if (arbolActual != null) {
			preordenOpSR(lista, arbolActual.getHi());
			if ((arbolActual.getNombre().equals("NOT") ||
				arbolActual.getNombre().equals("MP") ||
				arbolActual.getNombre().equals("MM") ||
				arbolActual.getNombre().equals("DU") ||
				arbolActual.getNombre().equals("EQ")) &&
				arbolActual.getPadre() != null)
				lista.add(arbolActual);
			preordenOpSR(lista, arbolActual.getHd());
		}
		
	}

	public ArrayList<Arbol> preordenOp1SR() {
		ArrayList<Arbol> preorden = new ArrayList<Arbol>();
		preordenOp1SR(preorden, this);
		
		return preorden;
	}
	
	private static void preordenOp1SR(ArrayList<Arbol> lista, Arbol arbolActual) {
		if (arbolActual != null) {
			preordenOp1SR(lista, arbolActual.getHi());
			if ((arbolActual.getNombre().equals("NOT") ||
				arbolActual.getNombre().equals("MP") ||
				arbolActual.getNombre().equals("MM")) &&
				arbolActual.getPadre() != null)
				lista.add(arbolActual);
			preordenOp1SR(lista, arbolActual.getHd());
		}
	}
	
	public ArrayList<Arbol> preordenOp2SR() {
		ArrayList<Arbol> preorden = new ArrayList<Arbol>();
		preordenOp2SR(preorden, this);
		
		return preorden;
	}
	
	private static void preordenOp2SR(ArrayList<Arbol> lista, Arbol arbolActual) {
		if (arbolActual != null) {
			preordenOp2SR(lista, arbolActual.getHi());
			if ((arbolActual.getNombre().equals("DU") ||
				arbolActual.getNombre().equals("EQ")) &&
				arbolActual.getPadre() != null)
				lista.add(arbolActual);
			preordenOp2SR(lista, arbolActual.getHd());
		}
	}
	
	public Object clone() {
		Arbol clon = new Arbol();
		if (hi != null)
			clon.hi = (Arbol) hi.clone(clon);
		else 
			clon.hi = null;
		if (hd != null)
			clon.hd = (Arbol) hd.clone(clon);
		else
			clon.hd = null;
		clon.padre = null;
		clon.esHi = esHi;
		clon.hoja = hoja;
		clon.nombre = nombre;
		clon.numNodos = numNodos;
		clon.profTotal = profTotal;
		clon.profundidad = profundidad;
		clon.raiz = raiz;
		
		return clon;
	}
	
	public Object clone(Arbol padre) {
		Arbol clon = new Arbol();
		if (hi != null)
			clon.hi = (Arbol) hi.clone(clon);
		else 
			clon.hi = null;
		if (hd != null)
			clon.hd = (Arbol) hd.clone(clon);
		else
			clon.hd = null;
		clon.padre = padre;
		clon.esHi = esHi;
		clon.hoja = hoja;
		clon.nombre = nombre;
		clon.numNodos = numNodos;
		clon.profTotal = profTotal;
		clon.profundidad = profundidad;
		clon.raiz = raiz;
		
		return clon;
	}

}
